/**
 * 跨操作系统统一接口: POSIX 文件系统适配
 */

#define _GNU_SOURCE
#define __USE_LARGEFILE64
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>

#include <stdio.h>
#include "unios.h"
#include "utils.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "unios"
#include "log4app.h"

// ======================================================================================
// 关于文件
// ======================================================================================

unios_file_t unios_file_null() {
    return -1;
}

bool unios_file_is_valid(unios_file_t uf) {
    return uf >= 0;
}

int64_t unios_file_size(const char *path) {
    struct stat fst;
    if(stat(path, &fst) < 0) { return -1; }
    return fst.st_size;
}

std_err_t unios_file_rename(const char *from, const char *to) {
    int err = rename(from, to);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

std_err_t unios_file_delete(const char *path) {
	int err = remove(path);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

unios_file_t unios_file_from_stdio_stream(FILE *fp) {
    return fileno(fp);
}

std_err_t unios_file_open(const char *path, unios_file_oflag_t oflag, unios_file_t *uf) {
    if(!path || !uf) return STD_ERR_INVALID_ARG;
    *uf = unios_file_null();

    if(oflag == UNIOS_FILE_OFLAG_READ) {
    	*uf = open(path, O_RDONLY);
    } else if(oflag == UNIOS_FILE_OFLAG_APPEND) {
        // 注意: 此处必须结合使用 O_RDWR 标志，而不是 O_WRONLY 标志，否则数据不会写入！
        *uf = open(path, O_RDWR | O_APPEND | O_CREAT, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_WRITE_TRUNCATE) {
    	*uf = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_READ_WRITE) {
    	*uf = open(path, O_RDWR, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_DEVICE_IO) {
    	*uf = open(path, O_RDWR, 0644);
    } else {
    	return STD_ERR_INVALID_ARG;
    }

    if(unios_file_is_valid(*uf)) return STD_ERR_OK;
    return STD_ERR_FAIL;
}

void unios_file_destroy(unios_file_t *uf) {
    if(!uf || !unios_file_is_valid(*uf)) return;
    close(*uf);
    *uf = -1;
}

int64_t unios_file_datasize(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return -1;
#if(__linux__ || __unix__)
    return (int64_t)lseek64(uf, 0, SEEK_END);
#else
    return (int64_t)lseek(uf, 0, SEEK_END);
#endif
}

std_err_t unios_file_sync(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return STD_ERR_INVALID_ARG;
    return (fsync(uf) == 0) ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_file_read(unios_file_t uf, void *buf, size_t nbyte, int64_t offset, size_t *rbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(rbyte) *rbyte = 0;
    
    do {
        // 设置文件当前偏移
		#if(__linux__ || __unix__)
			if(offset >= 0 && lseek64(uf, offset, SEEK_SET) != (ssize_t)offset) {
				loge("lseek() failed: %s\n", strerror(errno));
				err = STD_ERR_FAIL; break;
			}
		#else
			if(offset >= 0 && lseek(uf, offset, SEEK_SET) != (ssize_t)offset) {
				loge("lseek() failed: %s\n", strerror(errno));
				err = STD_ERR_FAIL; break;
			}
		#endif

        ssize_t dlen = read(uf, buf, nbyte);
        if(rbyte) *rbyte = dlen;
        if(dlen != nbyte)  {
            loge("read() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

std_err_t unios_file_write(unios_file_t uf, const void *buf, size_t nbyte, int64_t offset, size_t *wbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(wbyte) *wbyte = 0;
    
    do {
        // 设置文件当前偏移
		#if(__linux__ || __unix__)
			if(offset >= 0 && lseek64(uf, offset, SEEK_SET) != (ssize_t)offset) {
				loge("lseek() failed: %s\n", strerror(errno));
				err = STD_ERR_FAIL; break;
			}
		#else
			if(offset >= 0 && lseek(uf, offset, SEEK_SET) != (ssize_t)offset) {
				loge("lseek() failed: %s\n", strerror(errno));
				err = STD_ERR_FAIL; break;
			}
		#endif

        ssize_t dlen = write(uf, buf, nbyte);
        if(wbyte) *wbyte = dlen;
        if(dlen != nbyte)  {
            loge("write() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

CCC_WEAK void* unios_file_mmap(unios_file_t uf, uint64_t offset, uint64_t size, uint64_t flags1, uint64_t flags2, void **mmptr/*out*/, uint64_t *mmsize/*out*/) {
	// 不支持
    return NULL;
}

CCC_WEAK void unios_file_munmap(void *mmptr, uint64_t mmsize) {
	// 不支持
}

CCC_WEAK int unios_file_ioctl(unios_file_t uf, int cmd, void *opt, size_t opt_size) {
	// 不支持
    return -1;
}
